/**
 * 获取一个目录下vue文件或单个vue文件中，所有展示的中文字符，并替换为key
 */
const { strict } = require('assert');
const fs = require('fs');
const path = require('path');

/** 递归的处理vue文件 */
async function recursiveProcessFile (filePath, oriBase, newBase, configObj, handleJs) {
    const stat = fs.statSync(filePath);
    if (stat.isFile()) {
        const ext = path.extname(filePath);
        if (ext === '.vue') {
            const content = fs.readFileSync(filePath, 'utf-8');
            const filename = filePath.substring(filePath.lastIndexOf(path.sep) + 1, filePath.lastIndexOf('.vue'));
            const outputPath = filePath.replace(oriBase, newBase);
            // 处理vue文件
            handleChineseInVue(filename, outputPath, content, configObj, handleJs);
        } else {
            const outputPath = filePath.replace(oriBase, newBase);
            // 直接复制该文件
            fs.copyFileSync(filePath, outputPath);
        }
    } else if (stat.isDirectory()) {
        const files = fs.readdirSync(filePath);
        const newPath = filePath.replace(oriBase, newBase);
        fs.mkdirSync(newPath);
        files.forEach((fname) => recursiveProcessFile(path.join(filePath, fname), oriBase, newBase, configObj, handleJs));
    } else {
        console.warn('不是文件也不是目录')
    }
}

/** 解析出template, script, style三个区块的行范围 */
function parseVueBlock(lines) {
    const templateRange = [-1, -1], scriptRange = [-1, -1], styleRange = [-1, -1];
    lines.forEach((line, index) => {
        line = line.trim();
        // 首个<template开头的行，才是vue模版起始行
        if (templateRange[0] === -1 && line.startsWith('<template')) {
            templateRange[0] = index;
        }
        // 最后一个</template>结尾的行，是vue模版结束行
        if (line.endsWith('</template>')) {
            templateRange[1] = index;
        }
        if (scriptRange[0] === -1 && line.startsWith('<script')) {
            scriptRange[0] = index;
        }
        if (line.endsWith('</script>')) {
            scriptRange[1] = index;
        }
        if (styleRange[0] === -1 && line.startsWith('<style')) {
            styleRange[0] = index;
        }
        if (line.endsWith('</style>')) {
            styleRange[1] = index;
        }
    })
    return { templateRange, scriptRange, styleRange };
}

/** 处理单个vue的汉化 */
async function handleChineseInVue(fileKey, path, content, configObj, handleJs = true) {
    if (!configObj.hasOwnProperty(fileKey)) {
        configObj[fileKey] = {};
    }
    fs.writeFileSync(path, '');
    const lines = content.split('\n');
    // 1. 解析出template, script, style三个区块的行号
    const { templateRange, scriptRange, styleRange } = parseVueBlock(lines);
    console.log(`【${fileKey}】-script区块为${templateRange}, script区块为${scriptRange}, style区块为${styleRange}`)
    // 2. 挨行提取中文
    lines.forEach((line, index) => {
        // a. 注释行不处理
        if (line.trim().startsWith('<!--') || line.trim().startsWith('//') || line.trim().startsWith('/*')) {
            fs.appendFileSync(path, line + '\n');
            return;
        }
        // b. 处理template中的中文（把中文都替换为key）
        if (index > templateRange[0] && index < templateRange[1]) {
            // b.1 找出属性中的连续中文
            line = propChineseReplace(line, true, configObj, fileKey);
            line = propChineseReplace(line, false, configObj, fileKey);
            // b.2 找出jsx中的连续中文
            line = jsxChineseReplace(line, configObj, fileKey);
        }
        // c. 处理js中的中文
        if (index > scriptRange[0] && index < scriptRange[1]) {
            if (handleJs) {
                // 找出js中的中文段落
                line = javaScriptChineseReplace(line, configObj, fileKey);
            }
        }
        // 该行写入到文件中
        fs.appendFileSync(path, line + '\n');
    });
}

function propChineseReplace(line, singleQuote, configObj, fileKey) {
    let cnList = [];
    // 找出属性中的连续中文
    const regex = singleQuote ? / [A-Za-z0-9_-]{1,}="([\u4e00-\u9fa5]{1,})"/g : / [A-Za-z0-9_-]{1,}='([\u4e00-\u9fa5]{1,})'/g;
    const it = line.matchAll(regex);
    let result = it.next();
    while (!result.done) {
        if (result.value) {
            const matched = result.value[0];
            const chinese = result.value[1];
            const quote = singleQuote ? "'" : '"'
            const replaced = ` :${matched.substring(1)}`.replace(chinese, `$t(${quote}${fileKey}.${chinese}${quote})`)
            cnList.push({ ori: matched, dest: replaced });
            // 存入配置中
            configObj[fileKey][chinese] = chinese;
        }
        result = it.next();
    }
    cnList.reverse().forEach(({ori, dest}) => {
        // 将该行中的中文替换为i18n的方法
        line = line.replace(ori, dest)
    })
    return line;
}

function jsxChineseReplace(line, configObj, fileKey) {
    let cnList = [];
    // 找出所有的连续中文
    const it = line.matchAll(/[\u4e00-\u9fa5]{1,}/g);
    let result = it.next();
    while (!result.done) {
        if (result.value) {
            const chinese = result.value[0];
            const input = result.value.input;
            const idx = result.value.index;
            const prevChar = input[idx - 1];
            const nextChar = input[idx + chinese.length];
            // 如果为.{中文}"或者.{中文}'，则代表是前一步处理过的
            if (prevChar === '.' && (nextChar === '"' || nextChar === "'")) {
                // 不处理
            } else {
                cnList.push({ ori: chinese, dest:  `{{ $t("${fileKey}.${chinese}") }}`, startIndex: idx});
                // 存入配置中
                configObj[fileKey][chinese] = chinese;
            }
        }
        result = it.next();
    }
    // 从后往前查找替换
    cnList.reverse().forEach(({ori, dest, startIndex}) => {
        const str1 = line.substring(0, startIndex);
        const str2 = line.substring(startIndex);
        // 将该行中的中文替换为i18n的方法
        line = str1 + str2.replace(ori, dest);
    })
    return line;
}

function javaScriptChineseReplace(line, configObj, fileKey) {
    let cnList = [];
    // 找出所有的连续中文
    const it = line.matchAll(/['"]([\u4e00-\u9fa5]{1,}[,，.。:：;；!！?？、<《>》\(\)（）]?[\u4e00-\u9fa5]{0,})['"]/g);
    let result = it.next();
    while (!result.done) {
        if (result.value) {
            const matched = result.value[0];
            const chinese = result.value[1];
            cnList.push({ ori: matched, dest:  `this.$t("${fileKey}.${chinese}")`});
            // 存入配置中
            configObj[fileKey][chinese] = chinese;
        }
        result = it.next();
    }
    cnList.reverse().forEach(({ori, dest}) => {
        // 将该行中的中文替换为i18n的方法
        line = line.replace(ori, dest)
    })
    return line;
}

async function main(argv) {
    if (argv.length < 3) {
        console.error('请传入文件或目录地址');
        return;
    }
    // 重新创建输出目录
    if (fs.existsSync('./output')) {
        fs.rmSync('./output', { recursive: true, force: true });
    }
    fs.mkdirSync('./output');
    const outputDir = path.resolve('./output');

    const filePath = path.resolve(argv[2]);

    const handleJs = (argv[3] && argv[3] === '-js') || false;

    const configObj = {};
    recursiveProcessFile(filePath, path.dirname(filePath), outputDir, configObj, handleJs);
    fs.writeFileSync(path.join(outputDir, 'cn.json'), JSON.stringify(configObj, null, 2));
}

try {
    main(process.argv);
} catch (e) {
    console.error(e);
}